package com.example.hutooldemo;

import cn.hutool.core.lang.Assert;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.IOUtils;
import com.aliyun.oss.model.*;
import com.example.hutooldemo.properties.CommonProperties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

@Component
public class OssUtil {

    @Autowired
    private CommonProperties commonProperties;

    private static OSSClient client = null;

    private static Logger logger = LoggerFactory.getLogger(OssUtil.class);

    private OSSClient createOSSClient() {
        if (client == null) {
            ClientConfiguration conf = new ClientConfiguration();
            // 设置TCP连接超时为1分钟
            conf.setConnectionTimeout(60 * 1000);
            conf.setConnectionTTL(600 * 1000);
            // 设置最大的重试次数为2
            conf.setMaxErrorRetry(2);
            // 设置Socket传输数据超时的时间为15秒
            conf.setSocketTimeout(15 * 1000);
            client = new OSSClient(commonProperties.getOss().getEndpoint(), commonProperties.getOss().getAccessKeyId(), commonProperties.getOss().getAccessKeySecret(), conf);
        }
        return client;
    }


    /**
     * 文件是否存在
     *
     * @param ossKey
     * @return
     */
    public boolean exists(String ossKey) {

        return exists(ossKey, commonProperties.getOss().getBucket());
    }


    /**
     * 文件是否存在
     *
     * @param ossKey
     * @param bucketName
     * @return
     */
    public boolean exists(String ossKey, String bucketName) {
        AtomicBoolean result = new AtomicBoolean(false);
        Optional.ofNullable(readObject(ossKey, bucketName)).ifPresent((e) -> {
            try {
                result.set(true);
            } catch (Exception ex) {
                logger.error(ex.getMessage());
            } finally {
                try {
                    e.close();
                } catch (IOException ex) {
                    logger.error("close error", ex);
                }
            }
        });
        return result.get();
    }


    /**
     * 读取文件字节数组内容
     *
     * @param ossKey     文件地址
     * @param bucketName bucket名称
     * @return 字节数组
     */
    public byte[] readByte(String ossKey, String bucketName) {
        return Optional.ofNullable(readObject(ossKey, bucketName)).map((e) -> {
            try {
                byte[] file = IOUtils.readStreamAsByteArray(e.getObjectContent());
                return file;
            } catch (IOException e1) {
                logger.error(e1.getMessage());
            } finally {
                try {
                    e.close();
                } catch (IOException ex) {
                    logger.error("close error", ex);
                }
            }
            return null;
        }).orElse(null);
    }

    /**
     * @param ins      InputStream类型文件内容
     * @param fileType 文件后缀名
     * @param key      文件要保存的路径
     * @return boolean
     * @MethodName: putObject
     * @Description: 上传文件
     */
    public boolean uploadFile(InputStream ins, String fileType, String key) {

        return uploadFile(ins, fileType, key, commonProperties.getOss().getBucket());
    }

    /**
     * @param in        InputStream类型文件内容
     * @param suffixName 文件后缀名
     * @param prefixName 保存在oss的前缀包名
     * @return boolean
     * @MethodName: putObject
     * @Description: 上传文件
     */
    public String uploadFile(String prefixName, String suffixName, InputStream in) {
        //重新生成文件名
        String fileName = prefixName + "/" + UUID.randomUUID() + suffixName;
        String fullPath = null;
        if (commonProperties.getOss().getUrl().endsWith("/")) {
            fullPath = commonProperties.getOss().getUrl() + fileName;
        } else {
            fullPath = commonProperties.getOss().getUrl() + "/" + fileName;
        }
        boolean flag = uploadFile(in, suffixName, fileName);
        Assert.isTrue(flag, "上传失败");
        return fullPath;
    }

    public String uploadFile(String prefixName, String suffixName, byte[] bytes) {
        return uploadFile(prefixName, suffixName, new ByteArrayInputStream(bytes));
    }

    /**
     * @param ins      InputStream类型文件内容
     * @param fileType 文件后缀名
     * @param key      文件要保存的路径
     * @param buket    文件要保存到的bucket
     * @return boolean
     * @MethodName: putObject
     * @Description: 上传文件
     */
    public boolean uploadFile(InputStream ins, String fileType, String key, String buket) {
        boolean putFileSuccess = false;
        try {
            // 创建上传Object的Metadata
            ObjectMetadata meta = new ObjectMetadata();
            // 被下载时网页的缓存行为
            meta.setCacheControl("no-cache");
            // 设置上传内容类型
            meta.setContentDisposition(fileType);
            //创建上传请求
            PutObjectRequest request = new PutObjectRequest(buket, key, ins, meta);
            createOSSClient().putObject(request);
            putFileSuccess = true;
        } catch (Exception oe) {
            logger.error("uploadFile error:", oe);
            return putFileSuccess;
        } finally {
            try {
                ins.close();
            } catch (IOException e) {
                logger.error("close io error:", e);
            }
        }
        return putFileSuccess;
    }


    /**
     * @param file       字节数组格式的文件内容
     * @param fileType   文件后缀
     * @param ossKey     文件要保存的路径
     * @param bucketName 文件要保存到的bucket
     * @return boolean
     * @MethodName: uploadFile
     * @Description: OSS单文件上传
     */
    public boolean uploadFile(byte[] file, String fileType, String ossKey, String bucketName) {
        return uploadFile(new ByteArrayInputStream(file), fileType, ossKey, bucketName);
    }

    /**
     * 删除文件
     *
     * @param ossKey
     * @param bucketName
     * @return
     */
    public boolean deleteFile(String ossKey, String bucketName) {
        if (StringUtils.isEmpty(ossKey)) {
            return false;
        }
        try {
            GenericRequest request = new DeleteObjectsRequest(bucketName).withKey(ossKey);
            createOSSClient().deleteObject(request);
        } catch (Exception e) {
            logger.error("删除文件失败 :", e);
            return false;
        }
        return true;
    }

    /**
     * @param ossKeys 需要删除的文件ossKeys
     * @return int 成功删除的个数
     * @MethodName: batchDeleteFiles
     * @Description: 批量文件删除(较快)：适用于相同endPoint和BucketName
     */
    public int deleteFile(List<String> ossKeys) {
        return deleteFile(ossKeys, commonProperties.getOss().getBucket());
    }

    /**
     * @param ossKeys    需要删除的文件ossKeys
     * @param bucketName 需要删除的文件bucketName
     * @return int 成功删除的个数
     * @MethodName: batchDeleteFiles
     * @Description: 批量文件删除(较快)：适用于相同endPoint和BucketName
     */
    public int deleteFile(List<String> ossKeys, String bucketName) {
        //成功删除的个数
        int deleteCount = 0;
        if (StringUtils.isEmpty(bucketName)) {
            return deleteCount;
        }
        if (ossKeys.size() <= 0) {
            return deleteCount;
        }
        try {
            DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName).withKeys(ossKeys);
            DeleteObjectsResult result = createOSSClient().deleteObjects(request);
            deleteCount = result.getDeletedObjects().size();
        } catch (OSSException oe) {
            logger.error("批量删除文件失败OSSException :", oe);
            throw new RuntimeException("OSS服务异常:", oe);
        } catch (ClientException ce) {
            logger.error("批量删除文件失败ClientException :", ce);
            throw new RuntimeException("OSS客户端异常:", ce);
        }
        return deleteCount;

    }


    private OSSObject readObject(String ossKey, String bucketName) {
        if (StringUtils.isEmpty(bucketName)) {
            return null;
        }
        OSSObject object = null;
        try {
            object = createOSSClient().getObject(bucketName, ossKey);
        } catch (RuntimeException e) {
            logger.error("文件:" + ossKey + ", 不存在OSS上。" + e.getMessage(), e);
        }
        return object;
    }

}
